package amd64

import (
	"fmt"
	"strconv"
	"sync"
	"sync/atomic"

	"gitee.com/u-language/u-language/ucom/ast"
	"gitee.com/u-language/u-language/ucom/enum"
)

// 栈分配记录
type Stack struct {
	allmap *sync.Map //key=string value=StackVarInfo
	size   int64
}

func NewStack(sbt *ast.Sbt) *Stack {
	var ret *Stack = new(Stack)
	ret.allmap = new(sync.Map)
	ret.size = 0
	sbt.Range(func(key string, value ast.SymbolInfo) bool {
		if !(value.Kind == enum.SymbolVar || value.Kind == enum.SymbolNoParameVar) {
			return true
		}
		var IsStackAlloc bool
		if v, ok := value.Info.(*ast.VarNode); ok {
			IsStackAlloc = v.IsFunc
		} else {
			IsStackAlloc = value.Info.(*ast.VarInfoSbt).IsStackAlloc
		}
		if IsStackAlloc {
			size := ast.RetTypeSize(value.Type())
			offset := atomic.AddInt64(&ret.size, size)
			ret.allmap.Store(key, NewStackVarInfo(offset))
		}
		return true
	})
	return ret
}

// 查询是否记录了变量
//   - name是变量名
func (s *Stack) HaveVar(name string) StackVarInfo {
	v, ok := s.allmap.Load(name)
	if ok {
		return v.(StackVarInfo)
	}
	panic(fmt.Errorf("不存在的局部变量 %s", name))
}

// 栈变量信息
type StackVarInfo struct {
	//偏移量
	offset int64
}

func NewStackVarInfo(offset int64) StackVarInfo {
	return StackVarInfo{offset: offset}
}

func (info StackVarInfo) IsZero() bool {
	return info.offset == 0
}

// 分配函数栈
func StackAllocText(size int64) TextNode {
	var ret = make([]TextNode, 3)
	ret[0] = NewSrcNumDestReg(RSP, strconv.FormatInt(size+8, 10), SUB)
	ret[1] = NewSrcRegDestRegOffset(RBP, RBP, -8, MOV)
	ret[2] = NewSrcRegOffsetDestReg(RBP, RBP, -8, LEA)
	return NewStackAllocKind(ret)
}

// 释放函数栈
func StackFreeText(size int64) TextNode {
	var ret = make([]TextNode, 2)
	ret[0] = NewSrcRegMemDestReg(RBP, RBP, MOV)
	ret[1] = NewSrcNumDestReg(RSP, strconv.FormatInt(size+8, 10), ADD)
	return NewStackFreeKind(ret)
}
